import java.util.Random;

/*
 * genetic algorithm
 * considerations:
 * 1) cross over method: single point? two point? (how to choose the point? randomly? fixed?)  uniform crossover?
 * 2) mutation rate ? 
 */
public class GeneticAlgorithm {
	static Random rand = new Random();

	// randomly choose some bit and change their bit to the opposite
	static void mutate(double mutation_rate, ColorArea ca) {
		mutate(mutation_rate, ca.gene);
	}

	static void mutate(double mutation_rate, Gene gene) {
		mutate(mutation_rate, gene.gene);

	}

	static void mutate(double mutation_rate, int[] gene) {
		double x = (int) (mutation_rate * gene.length);
		int count;
		if (x == 0) {
			count = 0;
		} else if (x < 1) {
			count = 1;
		} else {
			count = (int) x;
		}
		int index;
		while (count != 0) {
			count--;
			index = ColorArea.rand.nextInt(gene.length);
			while (index <= 23) {
				index = ColorArea.rand.nextInt(gene.length);
			}
			gene[index] = (gene[index] + 1) % 2;
		}
	}

	static void cross_over(ColorArea father, ColorArea mother) {
		cross_over(father.gene, mother.gene);
	}

	static void cross_over(Gene father, Gene mother) {
		cross_over(father.gene, mother.gene);
	}

	static void cross_over_subarea_level(ColorArea father, ColorArea mother) {
		cross_over_subarea_level(father.gene, mother.gene);
	}

	static void cross_over_subarea_level(Gene father, Gene mother) {
		cross_over_subarea_level(father.gene, mother.gene);
	}

	// second version, still single point, but at the level of each subarea
	static void cross_over_subarea_level(int[] father, int[] mother) {
		int[] mother_temp = new int[24];
		int[] father_temp = new int[24];
		int head, tail;
		for (int i = 1; i < ColorArea.numSubArea; i++) {
			head = i * 24;
			tail = head + 23;
			father_temp = Gene.getSubArray(head, tail, father);
			mother_temp = Gene.getSubArray(head, tail, mother);
			cross_over(father_temp, mother_temp);
			int index = 0;
			for (int j = head; j < tail; j++) {
				mother[head] = mother_temp[index++];
			}
		}
	}

	static void cross_over_random_singlePoint_color_area_level(
			ColorArea father, ColorArea mother) {
		cross_over_random_singlePoint_color_area_level(father.gene, mother.gene);

	}

	static void cross_over_random_singlePoint_color_area_level(Gene father,
			Gene mother) {
		cross_over_random_singlePoint_color_area_level(father.gene, mother.gene);
	}

	static void cross_over_random_singlePoint_color_area_level(int[] father,
			int[] mother) {
		cross_over_random_single_point(father, mother);
	}

	// first version that use a single fix point(middle point) cross over
	// or should I cross over at the level of each color;
	static void cross_over(int[] father, int[] mother) {

		if (father.length != mother.length) {
			System.out.println("Error, the two gene should have equal length");
			return;
		}
		int[] boy = new int[father.length];
		// int[] girl = new int[father.length];

		int cross_over_point = (int) (0.5 * father.length);
		for (int i = 0; i < father.length; i++) {
			if (i <= cross_over_point) {
				boy[i] = father[i];
				// girl[i] = mother[i];

			} else {
				boy[i] = mother[i];
				// girl[i] = father[i];
			}
		}
		// father = boy;
		mother = boy;
	}

	// single point random version(of course, don't change the main color)
	static int[] cross_over_random_single_point(int[] father, int[] mother) {

		if (father.length != mother.length) {
			System.out.println("Error, the two gene should have equal length");
			return null;
		}
		int[] boy = new int[father.length];
		// int[] girl = new int[father.length];

		// System.out.println("before the random cross over:_____________");
		// Gene.show_gene(father);
		// Gene.show_gene(mother);

		int cross_over_point = rand.nextInt(father.length);
		// System.out.println("cross_over_point " + cross_over_point);
		for (int i = 0; i < father.length; i++) {
			if (i <= cross_over_point) {
				boy[i] = father[i];
				// girl[i] = mother[i];

			} else {
				boy[i] = mother[i];
				// girl[i] = father[i];
			}
		}
		// father = boy;
		mother = boy;
		return boy;
		// System.out.println("after the random cross over:__________________");
		// Gene.show_gene(father);
		// Gene.show_gene(mother);
	}

	static void cross_over_random_single_point_subarea_level(ColorArea father,
			ColorArea mother) {
		cross_over_random_single_point_subarea_level(father.gene, mother.gene);

	}

	static void cross_over_random_single_point_subarea_level(Gene father,
			Gene mother) {

		// System.out.println("before the random crossover______________________");
		// mother.show_gene();
		// System.out.println("______________________");

		cross_over_random_single_point_subarea_level(father.gene, mother.gene);

		// System.out.println("afther the random crossover______________________");
		// mother.show_gene();
		// System.out.println("______________________");

	}

	// cross over at randomly single point at the sub area level
	static void cross_over_random_single_point_subarea_level(int[] father,
			int[] mother) {
		int[] mother_temp = new int[24];
		int[] father_temp = new int[24];
		int head = 24, tail = 47;
		for (int i = 1; i < ColorArea.numSubArea; i++) {

			father_temp = Gene.getSubArray(head, tail, father);
			mother_temp = Gene.getSubArray(head, tail, mother);
			System.out.println();
			System.out.println("before the cross over__________");
			Gene.show_gene(mother_temp);
			mother_temp = cross_over_random_single_point(father_temp,
					mother_temp);
			// cross_over_random_single_point(father_temp, mother_temp);
			System.out.println("after the cross over__________");
			Gene.show_gene(mother_temp);
			int index = 0;
			for (int j = head; j <= tail; j++) {
				mother[j] = mother_temp[index++];
			}
			head = tail + 1;
			tail = head + 23;
		}
	}

	static void cross_over_random_single_point_random_color_level(
			ColorArea father, ColorArea mother) {
		cross_over_random_single_point_random_color_level(father.gene,
				mother.gene);

	}

	static void cross_over_random_single_point_random_color_level(Gene father,
			Gene mother) {
		cross_over_random_single_point_random_color_level(father.gene,
				mother.gene);

	}

	static void cross_over_random_single_point_random_color_level(int[] father,
			int[] mother) {

		int[] mother_temp;
		int[] father_temp;
		int head = 24, tail = 31;
		for (int i = 3; i < ColorArea.numSubArea * 3; i++) {

			father_temp = Gene.getSubArray(head, tail, father);
			mother_temp = Gene.getSubArray(head, tail, mother);
			// System.out.println();
			// System.out.println("before the cross over__________");
			// Gene.show_gene(mother_temp);
			mother_temp = cross_over_random_single_point(father_temp,
					mother_temp);
			// System.out.println("after the cross over__________");
			// Gene.show_gene(mother_temp);
			int index = 0;
			for (int j = head; j <= tail; j++) {
				mother[j] = mother_temp[index++];
			}
			head = tail + 1;
			tail = head + 7;
		}

	}

	// uniform cross over
	static void cross_over_uniform(ColorArea father, ColorArea mother) {
		cross_over_uniform(father.gene, mother.gene);
	}

	static void cross_over_uniform(Gene father, Gene mother) {
		mother.gene = cross_over_uniform(father.gene, mother.gene);
	}

	static int[] cross_over_uniform(int[] father, int[] mother) {
		int[] child = new int[mother.length];
		for (int i = 0; i < child.length; i++) {
			if (i < 23) {
				child[i] = father[i];

			} else {
				if (rand.nextBoolean()) {
					child[i] = father[i];
				} else {
					child[i] = mother[i];
				}
			}

		}
		return child;

	}

}
